﻿namespace Blaze.Editor.LuaExtensions
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.IO;
	using Framework;
	using Framework.LuaExtensions;
	using UnityEditor;
	using UnityEditorInternal;
	using UnityEngine;
	/// <summary>
	/// 用于扩展<see cref="LuaBehaviour"/>在Inspector中的显示。
	/// </summary>
	[CustomEditor(typeof(LuaBehaviour))]
	public class LuaBehaviourInspector : Editor
	{
		static readonly string LUA_FILE_EXT = ".lua";

		class LuaRootInfo
		{
			public string Name;
			public string FullPath; //总是用/分割，不使用\。并且，结尾没有/ 。
		}

		class LuaFileInfo
		{
			public LuaRootInfo Root;
			public string[] Files;        //相对目录，没有.lua结尾
		}

		static IEnumerable<LuaRootInfo> s_GetLuaRoots()
		{
			string projectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "/Assets".Length);
			yield return new LuaRootInfo() { Name = "Platform", FullPath = projectPath + "/Lua" };
		}

	    LuaRootInfo[] m_LuaRoots = null;
		LuaRootInfo[] GetLuaRoots()
		{
			if (m_LuaRoots == null)
				m_LuaRoots = s_GetLuaRoots().ToArray();
			return m_LuaRoots;
		}

		LuaFileInfo[] m_LuaFiles = null;

	    GUIStyle m_DropZoneStyle;
		//如果obj是一个Texture2D asset，则尝试转换成一个Sprite
		static void TryConvertToSpriteAsset(ref UnityEngine.Object obj)
		{
			if (obj is Texture2D)
			{
				string path = AssetDatabase.GetAssetPath(obj);
				if (!string.IsNullOrEmpty(path))
				{
					//当一个texture2d只包含一个sprite的时候，才转换成sprite
					//否则认为你要拖动的就是一个texture2d
					var objs = AssetDatabase.LoadAllAssetsAtPath(path);
					int spriteCount = objs.Count(x => x is Sprite);
					if (spriteCount == 1)
					{
						obj = objs.First(x => x is Sprite);
					}
				}
			}
		}
		private void ShowDropZone()
		{
			float RECT_HEIGHT = 30.0f;
			var content = new GUIContent("把东西拖到这里可以一次性添加好多东西");
			int controlID = GUIUtility.GetControlID(998654321, FocusType.Passive);
			Rect rect = GUILayoutUtility.GetRect(content, GUI.skin.box, GUILayout.ExpandWidth(true), GUILayout.Height(RECT_HEIGHT));

			var eventType = Event.current.type;
			if (m_DropZoneStyle == null)
			{
				m_DropZoneStyle = new GUIStyle(GUI.skin.box);
				m_DropZoneStyle.alignment = TextAnchor.MiddleCenter;
			}
			switch (eventType)
			{
				case EventType.DragUpdated:
				case EventType.DragPerform:
					if (rect.Contains(Event.current.mousePosition))
					{
						DragAndDrop.activeControlID = controlID;
						DragAndDrop.visualMode = DragAndDropVisualMode.Copy;

						if (eventType == EventType.DragPerform && DragAndDrop.objectReferences.Length > 0)
						{
							Undo.RecordObject(mBehaviour, "向LuaBehaviour添加了一堆东西");
							foreach (var obj2 in DragAndDrop.objectReferences)
							{
								var obj = obj2;
								string name = obj.name;
								LuaFieldType type = EditorUtility.IsPersistent(obj) ? LuaFieldType.Asset : LuaFieldType.GameObject;
								//如果拖入的object是一个Texture2D，并且只包含一个sprite，则可能想要拖入的是一个Sprite。
								//自动转换成Sprite
								if (type == LuaFieldType.Asset && obj is Texture2D)
								{
									TryConvertToSpriteAsset(ref obj);
								}

								//if name is duplicatted, then create a good name.
								if (mBehaviour.Fields.Any(x => x.K == name))
								{
									name = ObjectNames.GetUniqueName(mBehaviour.Fields.Select(x => x.K).ToArray(), name);
								}

								mBehaviour.Fields.Add(new LuaFieldPair()
								{
									K = name,
									T = type,
									V = obj
								});
							}
						}
						Event.current.Use();
					}
					break;
			}


			if (Event.current.type == EventType.Repaint)
			{
				var oldColor = GUI.color;
				if (DragAndDrop.activeControlID == controlID)
				{
					content.text = "正在准备添加" + DragAndDrop.objectReferences.Length + "个东西";
					//GUI.color = Color.gray;
					m_DropZoneStyle.normal.textColor = Color.black;
				}
				else
				{
					//GUI.color = Color.black;
					m_DropZoneStyle.normal.textColor = Color.gray;
				}
				m_DropZoneStyle.Draw(rect, content, controlID);
				GUI.color = oldColor;
			}

		}

		private void drawFilePath()
		{
		    using (var firstLine = new EditorGUILayout.HorizontalScope())
		    {
		        GUILayout.Label("Lua File Path", GUILayout.Width(96));
		        var oldBackgroundColor = GUI.backgroundColor;
                GUI.backgroundColor = IsFileExists() ? Color.green : Color.red;
		        var luaFilePath = EditorGUILayout.TextField(mBehaviour.LuaFilePath);
		        luaFilePath = PathUtility.FormatSeperator(luaFilePath);
		        if (luaFilePath != mBehaviour.LuaFilePath)
		        {
		            Undo.RecordObject(mBehaviour, "修改了lua文件路径");
		            mBehaviour.LuaFilePath = luaFilePath;
		        }
		        GUI.backgroundColor = oldBackgroundColor;
		        if (GUILayout.Button("...", mSearchButtonStyle, GUILayout.Width(32), GUILayout.Height(16f)))
		        {
		            var rect = firstLine.rect;
		            if (ChooseScriptWindow.Show(rect, (LuaBehaviour) target))
		                GUIUtility.ExitGUI();
		        }
		    }
		}

		private bool hasDuplicatedField(string fieldName)
		{
			if (mBehaviour.Fields == null)
				return false;
			if (string.IsNullOrEmpty(fieldName))
				return true;

			var count = 0;
			for (var i = 0; i < mBehaviour.Fields.Count; i++)
			{
				if (mBehaviour.Fields[i].K == fieldName)
				{
					if (count > 0)
						return true;
					count++;
				}
			}
			return false;
		}

	    protected virtual bool IsFileExists()
	    {
	        if (string.IsNullOrEmpty(mBehaviour.LuaFilePath))
	            return false;
	        foreach (var ri in GetLuaRoots())
	        {
	            if (File.Exists(ri.FullPath + "/" + mBehaviour.LuaFilePath + ".lua"))
	                return true;
	        }
	        return false;
	    }

		private LuaBehaviour mBehaviour;
		private ReorderableList mReorderableList;

		#region #Build-in methods

		public override void OnInspectorGUI()
		{
			mBehaviour = target as LuaBehaviour;
			if (mBehaviour == null)
			{
				base.OnInspectorGUI();
				return;
			}

		    if (mSearchButtonStyle == null)
		        mSearchButtonStyle = new GUIStyle(EditorStyles.miniButton);

            drawFilePath();
			mBehaviour.OnInspectorGUI();
			ShowDropZone();
		    if (mReorderableList != null)
		        mReorderableList.DoLayoutList();
        }

		private void OnEnable()
		{
			mBehaviour = (LuaBehaviour)target;
			mReorderableList = new ReorderableList(mBehaviour.Fields, typeof(LuaFieldPair), true, true, true, true)
			{
				drawHeaderCallback = onDrawHeader,
				drawElementCallback = onDrawElement,
				onAddCallback = onAddItem,
				onRemoveCallback = onRemoveItem
			};
		}

		private void OnDisable()
		{
			mReorderableList.drawHeaderCallback = null;
			mReorderableList.drawElementCallback = null;
			mReorderableList.onAddCallback = null;
			mReorderableList.onRemoveCallback = null;
			mReorderableList = null;
		}

		#endregion

		#region ReorderableList event handlers

		private void onRemoveItem(ReorderableList list)
		{
			Undo.RecordObject(target, "向LuaBehaviour删除了一个东西");
			mBehaviour.Fields.RemoveAt(list.index);
			EditorUtility.SetDirty(target);
		}

		private void onAddItem(ReorderableList list)
		{
			Undo.RecordObject(target, "向LuaBehaviour添加了一个东西");
			mBehaviour.Fields.Add((new LuaFieldPair
			{
				K = string.Empty,
				T = LuaFieldType.GameObject
			}));
			EditorUtility.SetDirty(target);
		}

	    private void onDrawElement(Rect rect, int index, bool isactive, bool isfocused)
	    {
	        var fieldPair = mBehaviour.Fields[index];
	        EditorGUI.BeginChangeCheck();

	        const int padding = 4;

	        var h = rect.height - padding;
	        var x = rect.x;
	        var y = rect.y + padding;

	        var typeRect = new Rect(x, y, 60, h);
	        x += typeRect.width + padding;

	        var maxKeyWidth = Mathf.Max(10.0f, (rect.width - typeRect.width - padding - padding) * 0.5f);
	        var keyRect = new Rect(x, y, 120, h);
	        if (keyRect.width > maxKeyWidth)
	            keyRect.width = maxKeyWidth;
	        x += keyRect.width + padding;
	        var valueRect = new Rect(x, y, rect.width - typeRect.width - keyRect.width - padding, h);

	        var newType = (LuaFieldType) EditorGUI.EnumPopup(typeRect, fieldPair.T);
	        if (!fieldPair.TryConvertTo(newType))
	        {
	            fieldPair.T = newType;
	            fieldPair.ClearValues();
	        }

	        var originColor = GUI.backgroundColor;
	        GUI.backgroundColor = hasDuplicatedField(fieldPair.K) ? Color.red : Color.green;

	        switch (fieldPair.T)
	        {
	            case LuaFieldType.GameObject:
	            {
	                var k = EditorGUI.TextField(keyRect, fieldPair.K);
	                valueRect.width -= 18;
	                var v = EditorGUI.ObjectField(valueRect, fieldPair.V, typeof(GameObject), true);
	                if (string.IsNullOrEmpty(k) && v != null)
	                    k = v.name;
	                fieldPair.K = k;
	                fieldPair.V = v;
                    drawComponentsDropdown(valueRect.x + valueRect.width, valueRect.y, ref fieldPair);
	                break;
	            }
	            case LuaFieldType.String:
	                fieldPair.K = EditorGUI.TextField(keyRect, fieldPair.K);
	                fieldPair.V1 = EditorGUI.TextField(valueRect, fieldPair.V1);
	                break;
	            case LuaFieldType.Asset:
	                fieldPair.K = EditorGUI.TextField(keyRect, fieldPair.K);
	                fieldPair.V = EditorGUI.ObjectField(valueRect, fieldPair.V, typeof(UnityEngine.Object), false);
	                break;
	            case LuaFieldType.Number:
	                fieldPair.K = EditorGUI.TextField(keyRect, fieldPair.K);
	                fieldPair.V2 = EditorGUI.FloatField(valueRect, fieldPair.V2);
	                break;
	            case LuaFieldType.Boolean:
	                fieldPair.K = EditorGUI.TextField(keyRect, fieldPair.K);
	                var toggleResult = EditorGUI.Toggle(valueRect, fieldPair.V2 != 0);
	                fieldPair.V2 = toggleResult ? 1 : 0;
	                break;
	            case LuaFieldType.LuaTable:
	            {
	                var k = EditorGUI.TextField(keyRect, fieldPair.K);
	                if (fieldPair.V == null || fieldPair.V is GameObject)
	                {
	                    fieldPair.T = LuaFieldType.GameObject;
	                    mBehaviour.Fields[index] = fieldPair;
	                }

	                if (fieldPair.V is Component)
	                {
	                    if (!(fieldPair.V is LuaBehaviour))
	                    {
	                        fieldPair.T = LuaFieldType.Component;
	                        mBehaviour.Fields[index] = fieldPair;
	                    }

	                    valueRect.width -= 18;
	                    var v = EditorGUI.ObjectField(valueRect, fieldPair.V, typeof(GameObject), true);
	                    if (string.IsNullOrEmpty(k) && v != null)
	                        k = v.name;

	                    fieldPair.K = k;
	                    drawComponentsDropdown(valueRect.x + valueRect.width, valueRect.y, ref fieldPair);
	                }

                    break;
	            }
	            case LuaFieldType.Component:
	            {
	                var k = EditorGUI.TextField(keyRect, fieldPair.K);
	                if (fieldPair.V == null || fieldPair.V is GameObject)
	                {
	                    fieldPair.T = LuaFieldType.GameObject;
	                    mBehaviour.Fields[index] = fieldPair;
	                }

	                if (fieldPair.V is Component)
	                {
	                    valueRect.width -= 18;
	                    var v = EditorGUI.ObjectField(valueRect, fieldPair.V, typeof(GameObject), true);
	                    if (string.IsNullOrEmpty(k) && v != null)
	                        k = v.name;

	                    fieldPair.K = k;
	                    drawComponentsDropdown(valueRect.x + valueRect.width, valueRect.y, ref fieldPair);
	                }

	                break;
	            }
	            default:
	                throw new ArgumentOutOfRangeException();
	        }

	        GUI.backgroundColor = originColor;

	        if (EditorGUI.EndChangeCheck())
	        {
	            Undo.RecordObject(mBehaviour, "修改了LuaBehaviour的属性");
	            mBehaviour.Fields[index] = fieldPair;
	            EditorUtility.SetDirty(target);
	        }
	    }

	    private static void drawComponentsDropdown(float x, float y, ref LuaFieldPair pair)
	    {
	        if (pair.V == null)
	            return;

            GUI.backgroundColor = Color.white;
	        var rect = new Rect(x, y, 16, 16);
	        var options = new List<string> {"--- Game Object Only ---"};
	        GameObject gameObject;
	        Component component;
	        if (pair.V is Component)
	        {
	            component = (Component) pair.V;
	            gameObject = component.gameObject;
	        }
	        else
	        {
	            component = null;
	            gameObject = (GameObject) pair.V;
	        }

	        var components = gameObject.GetComponents<Component>();
	        options.AddRange(components.Select(c => c.GetType().Name));
	        var currentIndex = Array.IndexOf(components, component) + 1;
	        var selectedIndex = EditorGUI.Popup(rect, currentIndex, options.ToArray());
	        if (currentIndex != selectedIndex)
	            GUI.changed = true;
	        if (selectedIndex == 0)
	        {
	            pair.T = LuaFieldType.GameObject;
	            pair.V = gameObject;
	        }
            else if (pair.T == LuaFieldType.LuaTable)
	        {
	            pair.V = components[selectedIndex - 1];
            }
            else
	        {
	            pair.T = LuaFieldType.Component;
	            pair.V = components[selectedIndex - 1];
	        }
	    }

		private void onDrawHeader(Rect rect)
		{
		    GUI.Label(rect, "Lua Fields");
		    rect.x += rect.width - 64;
		    rect.width = 64;
		    rect.height += 8;
		    mBehaviour.AutoInject = GUI.Toggle(rect, mBehaviour.AutoInject, "自动注入");
        }

		#endregion

	    private GUIStyle mSearchButtonStyle;
	}
}